﻿/*
 * CanonOscar 0.1
 * Written by (c) agehack a.k.a. GUnitSoldier
 * Released under GNU GPL (http://www.gnu.org/copyleft/gpl.html)
 * 
 * http://www.agehack.pro/oscar
 */
using System;
using System.Collections.Generic;
using System.Text;
using Agehack;
using System.Net;
using Grunwald.NOscar;
namespace Agehack.Icq
{
    public partial class CanonOscar
    {
        public partial class IcqClient
        {
            /// <summary>
            /// Processes an login/auth/negotiation sequence to open ICQ session.
            /// </summary>
            /// <param name="cliReq">User/Client info.</param>
            public void Connect(ref AuthRequest cliReq)
            {
                try
                {
                    cliReq.Error = null;
                    if (this.IsConnected)
                        throw new InvalidOperationException("Already connected. Need to Disconnect() first.");

                    authorized = false;
                    // Connect:
                    cliReq.Progress = AuthRequest.AuthStage.Connecting;
                    IPEndPoint ip = cliReq.GetServerIP();
                    connection.Connect(ip);

                    // Receive server version cookie
                    cliReq.Progress = AuthRequest.AuthStage.Connected;
                    ReceivePacket();    // -LAZY check it
                    
                    // Send authorization request
                    cliReq.Progress = AuthRequest.AuthStage.Authorization;
                    Flap f = cliReq.MakeFlap();
                    SendPacket(f);
                    ReceivePacket();
                    if (f == null)
                        if (!connection.IsConnected)
                            throw new Exception("Connection closed.");
                        else throw new NullReferenceException("No response received");
                    else
                    {
                        connection.Disconnect();// -FIXME temp
                        throw new NotImplementedException("Paket received! " + f.ToString());
                    }
                }
                catch (Exception ex)
                {
                    cliReq.Error = ex;
                    cliReq.Progress = AuthRequest.AuthStage.ErrorFailed;
                }                
            }

            /// <summary>
            /// Used to watch authorization progress in cient code (events),
            /// Saves info about login server and user auth data.
            /// </summary>
            public class AuthRequest : SnacPack
            {
                #region Fields & Properties
                public string LoginServerAddress;
                public int LoginServerPort;
                public string UserName;
                public string UserPassword;
                #endregion
                #region Constructors & Methods
                public AuthRequest(string serveraddress, string username, string password)
                {
                    LoginServerAddress = serveraddress;
                    LoginServerPort = 5190;
                    UserName = username;
                    UserPassword = password;

                    // This is not snac. Snac header will be skipped (SnacPack additional)
                    IsRawDataInside = true; 
                    RawDataFlapChannel = 0x01;  // New connection
                }
                public IPEndPoint GetServerIP()
                {
                    IPAddress ip = null;
                    if(!IPAddress.TryParse(LoginServerAddress, out ip))
                    {
                        Progress= AuthStage.HostnameLookup;
                        ip = Dns.GetHostAddresses(LoginServerAddress)[0];
                    }
                    return new IPEndPoint(ip, LoginServerPort);
                }
                public override Snac MakeSnac()
                {
                    TlvList tlvs = new TlvList();
                    tlvs.Add(0x0001, util.stringAsBytes(UserName));
                    tlvs.Add(0x0002, util.stringAsBytes(
                        util.xorString(UserPassword)));
                    tlvs.Add(0x0003, util.stringAsBytes(
                        "ICQ Client"));
                    tlvs.Add(0x0016, NetworkBitConverter.GetBytes(
                        (ushort)266));
                    tlvs.Add(0x0017, NetworkBitConverter.GetBytes(
                        (ushort)6));
                    tlvs.Add(0x0018, NetworkBitConverter.GetBytes(
                        (ushort)5));
                    tlvs.Add(0x0019, NetworkBitConverter.GetBytes(
                        (ushort)0));
                    tlvs.Add(0x001a, NetworkBitConverter.GetBytes(
                        (ushort)104));
                    tlvs.Add(0x0014, NetworkBitConverter.GetBytes(
                        (ushort)30007));
                    tlvs.Add(0x000f,
                        util.stringAsBytes("en"));
                    tlvs.Add(0x000e,
                        util.stringAsBytes("us"));

                    Snac s = new Snac(0, 0);    // this is not snac, we dont need header
                    s.AppendBody(
                        NetworkBitConverter.GetBytes((uint)0x00000001));    // Client version
                    foreach (Tlv t in tlvs.ToList())
                        s.AppendBody(t.GetBytes());
                    return s;
                }
                #endregion
                #region Authorization progress event / state
                public enum AuthStage
                {
                    /// <summary>
                    /// Unknown situation (or not started sequence).
                    /// </summary>
                    Unknown,
                    /// <summary>
                    /// Searching login server in DNS.
                    /// </summary>
                    HostnameLookup,
                    /// <summary>
                    /// Authorizer is connecting to login server.
                    /// </summary>
                    Connecting,
                    /// <summary>
                    /// Connection to login server ok.
                    /// </summary>
                    Connected,
                    /// <summary>
                    /// Authorizer is processing login.
                    /// </summary>
                    Authorization,
                    /// <summary>
                    /// Authorization done, we connecting to BOSS to start session.
                    /// </summary>
                    Reconnecting,
                    /// <summary>
                    /// Receiving service information from server.
                    /// </summary>
                    Negotiating,
                    /// <summary>
                    /// Operation done. Session started.
                    /// </summary>
                    ConnectedAuthorizedRead,
                    /// <summary>
                    /// Failed to connect/authorize.
                    /// </summary>
                    ErrorFailed
                }
                public delegate void ClientAuthorizationProgressChanged(AuthRequest clientUser, AuthStage status);
                public event ClientAuthorizationProgressChanged AuthorizationProgressChanged;
                private AuthStage progress;
                public AuthStage Progress
                {
                    get { return progress; }
                    set
                    {
                        onAuthProgressChange(value);
                    }
                }
                private void onAuthProgressChange(AuthStage status)
                {
                    progress = status;
                    if (AuthorizationProgressChanged != null)
                        AuthorizationProgressChanged(this, status);
                }
                public Exception Error;
                #endregion                
            }
        }
    }
}
